﻿Type.registerNamespace('TallComponents');

TallComponents.ThumbnailsList = function (element) {
    ///<summary>Displays a list of autosizing PDF thumbnails</summary>
    TallComponents.ThumbnailsList.initializeBase(this, [element]);
    this._baseUrl = null;
    this._pageCount = 0;
    this._targetControlId = null;
    this._activeCssClass = null;
    this._activeIndex = -1;
    this._targetControl = null;
    this._thumbs = null;
    this._thumbLoaded = null;
    this._loadQueue = null;
    this._loading = null;
    this._appLoadDelegate = null;
    this._lastScrollTop = null;
    this._delayTimer = null;
    this._autoScaleMode = 'none';
    this._fixElementPos = false;
    this._scale = 0.25;
};

//
// Create the prototype for the control.
//
TallComponents.ThumbnailsList.prototype = {
    initialize: function () {
        ///<summary>Initializes the control.</summary>
        ///<remarks>The initialize method initializes the control and sets the base <see cref="M:J#Sys.Component.isInitialized"/> property to true.</remarks>
        TallComponents.ThumbnailsList.callBaseMethod(this, "initialize");

        this._fixElementPos = (document.documentElement["getBoundingClientRect"]) ? true : false;
        this._findPosition = this._fixElementPos ? this._findPositionNew : this._findPositionOld;

        this._thumbs = new Array();
        this._thumbUrls = new Array();

        var element = this.get_element();
        var images = element.getElementsByTagName('img');
        for (i = 0; i < images.length; i++) {
            images[i].pageIndex = i;
            images[i].rotation = 0;
            this._thumbs.push(images[i]);
        }

        this._pageCount = this._thumbs.length;

        $addHandlers(element, { scroll: this._onScroll }, this);

        if (!this._hookupViewer()) {
            this._appLoadDelegate = Function.createDelegate(this, this._appLoad);
            Sys.Application.add_load(this._appLoadDelegate);
        }
    },

    dispose: function () {
        ///<summary>Removes the current control from the application.</summary>
        ///<remarks>The dispose method releases all resources from the <see cref="T:J#Sys.UI.Control"/> object, 
        ///unbinds it from its associated HTML Document Object Model (DOM) element, 
        ///and unregisters it from the application.</remarks>
        $clearHandlers(this.get_element());
        if (this._targetControl) { this._targetControl.remove_updated(this._onViewerUpdated); }
        if (this._appLoadDelegate) { Sys.Application.remove_load(this._appLoadDelegate); }
        if (this._loading) { $clearHandlers(this._loading); }
        TallComponents.ThumbnailsList.callBaseMethod(this, 'dispose');
    },

    _appLoad: function (sender, args) {
        Sys.Application.remove_load(this._appLoadDelegate);
        this._appLoadDelegate = null;
        this._hookupViewer();
    },

    _hookupViewer: function () {
        this._targetControl = $find(this._targetControlId);

        if (this._targetControl) {
            this._targetControl.add_updated(Function.createDelegate(this, this._onViewerUpdated));
            return true;
        }
      setTimeout(Function.createDelegate(this, this._hookupViewer), 500);
        return false;
    },

    _onScroll: function (e) {
        if (this._delayTimer) {
            clearTimeout(this._delayTimer);
        }
        this._delayTimer = setTimeout(Function.createDelegate(this, this._startUpdate), 500);
    },

    _onViewerUpdated: function () {
        ///<summary>Handles the viewer update event.</summary>
        //we need to have a current page index and a current layout mode
        var currentPageIndex = this._targetControl.get_pageIndex();
      var pageLayout = ''; // this._targetControl.get_pageLayout();

        //update the thumbnails taking into account a current layout mode
        switch (pageLayout) {
            case 'double':
                //only two pages at the viewer were updated, so lets update their thumbnails
                //the left page of a couple of the pages always has the even index
                var leftIndex = (currentPageIndex % 2 === 0) ? currentPageIndex : currentPageIndex - 1;
                //the right page of a couple of the pages has the next index
                var rightIndex = leftIndex + 1;
                
                this._updateThumbnailRotation(leftIndex);
                this._updateThumbnailRotation(rightIndex);
                break;
            case 'continuous':
            case 'continuousDouble':
                //all the pages were updated, so we need to update all the thumbnails
                if (this._targetControl._document.IsValid) {
                    for (var pageIndex = 0; pageIndex < this._targetControl._document.Pages.length; pageIndex++) {
                        this._updateThumbnailRotation(pageIndex);
                    }
                }
                break;
            default:
                //actually it means that the layout mode is 'single' and we update only the current page
                this._updateThumbnailRotation(currentPageIndex);
                break;
        }


        this._startUpdate();
    },

    _updateThumbnailRotation: function (thumbnailIndex) {
        ///<summary>Updates the rotation of a thumbnail with the specified index</summary>
        var thumbnail = this._thumbs[thumbnailIndex];
        if (thumbnail) {
         thumbnail.rotation = this._targetControl.getPageRotation(thumbnailIndex);
        }
    },

    _startUpdate: function () {
        if (this._delayTimer) {
            clearTimeout(this._delayTimer);
        }

        var element = this.get_element();
        var containerPos = this._findPosition(element);
        containerPos.top = containerPos.top + element.scrollTop;
        containerPos.left = containerPos.left + element.scrollLeft;
        containerPos.bottom = containerPos.top + element.clientHeight;
        containerPos.right = containerPos.left + element.clientWidth;

        if (!this._loadQueue) {
            this._loadQueue = new Array();
        }

        // hit detection
        if (this._thumbs) {
            for (i = 0; i < this._thumbs.length; i++) {
                var thumb = this._thumbs[i];
                if (!this._isLoaded(thumb)) {
                    var pos = this._findPosition(thumb);
                    if (this._fixElementPos) {
                        // apply fix for scroll position for newer findPosition implementation
                        pos.left += element.scrollLeft;
                        pos.top += element.scrollTop;
                    }
                    if ((pos.top + thumb.clientHeight) >= containerPos.top && pos.top < containerPos.bottom &&
                       (pos.left + thumb.clientWidth) >= containerPos.left && pos.left < containerPos.right) {
                        this._loadQueue.push(thumb);
                    }
                }
            }
        }

        // make sure the correct thumb is highlighted
        if (this._thumbs && this._thumbs.length > 0 && this._activeCssClass) {
            if (this._activeIndex != this._targetControl.get_pageIndex()) {
                if (this._activeIndex >= 0 && this._activeIndex < this._thumbs.length) {
                    Sys.UI.DomElement.removeCssClass(this._thumbs[this._activeIndex].parentNode, this._activeCssClass);
                }

                this._activeIndex = this._targetControl.get_pageIndex();

            var thumb = this._thumbs[this._activeIndex];
            var pos = this._findPosition(thumb);
            if (this._fixElementPos) {
               // apply fix for scroll position for newer findPosition implementation
               pos.left += element.scrollLeft;
               pos.top += element.scrollTop;
            }

            pos.bottom = pos.top + thumb.clientHeight;
            var isThumbFitVertically = pos.top >= containerPos.top && pos.bottom < containerPos.bottom;
            pos.right = pos.left + thumb.clientWidth;
            var isThumbFitHorisontally = pos.left >= containerPos.left && pos.right < containerPos.right;

            //calculate the vector delta that should be added to thumb position to get it fully visible
            var delta = { x: 0, y: 0 };

            if (!isThumbFitVertically) {
               //calculate Y component
               //if thumb's top is above the container's top 
               if (pos.top < containerPos.top) {
                  //thumb.top - container.top
                  delta.y = pos.top - containerPos.top;
               }
               else if (containerPos.bottom < pos.bottom) {
                  //if thumb's bottom is below the bottom point of the container = thumb.bottom - container.bottom
                  delta.y = pos.bottom - containerPos.bottom;
               }
            }

            if (!isThumbFitHorisontally) {
               //calculate X component
               //if thumb's left is behind the left border of the container
               if (pos.left < containerPos.left) {
                  //thumb.left - container.left
                  delta.x = pos.left - containerPos.left;
               }
               else if (containerPos.right < pos.right) {
                  //if thumb's right is behind the right border of the container = thumb.right - continer.right
                  delta.x = pos.right - containerPos.right;
               }
            }

            if (!(isThumbFitVertically && isThumbFitHorisontally)) {
               //add this delta to curretn pos and scroll to the new position
               var newpos = { x: element.scrollLeft + delta.x, y: element.scrollTop + delta.y }; //containerPos.top 
               this._scrollToThumbnail(newpos);
            }

                Sys.UI.DomElement.addCssClass(this._thumbs[this._activeIndex].parentNode, this._activeCssClass);
            }
        }

        this._loadNext();
    },

   _scrollToThumbnail: function (pos) {
      this.set_position(pos);
   },

   set_position: function (position) {
      ///<summary>Sets the position of the scrollbars.</summary>
      this.set_positionX(position.x);
      this.set_positionY(position.y);
   },

   set_positionX: function (xPosition) {
      ///<summary>Sets the position of the horisontal scrollbar.</summary>
      var container = this.get_element();
      container.scrollLeft = xPosition;
   },

   set_positionY: function (yPosition) {
      ///<summary>Sets the position of the vertical scrollbar.</summary>
      var container = this.get_element();
      container.scrollTop = yPosition;
   },

    _innerSize: function (elem) {
        var size = new Object();
        size.width = elem.clientWidth - this._safeParseInt(this._getStyle(elem, 'padding-left')) - this._safeParseInt(this._getStyle(elem, 'padding-right'));
        size.height = elem.clientHeight - this._safeParseInt(this._getStyle(elem, 'padding-top')) - this._safeParseInt(this._getStyle(elem, 'padding-bottom'));
        return size;
    },

    _chromeHeight: function (elem) {
        return this._safeParseInt(this._getStyle(elem, 'margin-top')) + this._safeParseInt(this._getStyle(elem, 'margin-bottom')) +
               this._safeParseInt(this._getStyle(elem, 'border-top-width')) + this._safeParseInt(this._getStyle(elem, 'border-bottom-width')) +
               this._safeParseInt(this._getStyle(elem, 'padding-top')) + this._safeParseInt(this._getStyle(elem, 'padding-bottom'));
    },

    _chromeWidth: function (elem) {
        return this._safeParseInt(this._getStyle(elem, 'margin-left')) + this._safeParseInt(this._getStyle(elem, 'margin-right')) +
               this._safeParseInt(this._getStyle(elem, 'border-left-width')) + this._safeParseInt(this._getStyle(elem, 'border-right-width')) +
               this._safeParseInt(this._getStyle(elem, 'padding-left')) + this._safeParseInt(this._getStyle(elem, 'padding-right'));
    },

    // See also http://ejohn.org/blog/getboundingclientrect-is-awesome/
    _findPositionNew: function (elem) {
        if (!elem) { return { top: 0, left: 0 }; }
        var box = elem.getBoundingClientRect();
        return { top: box.top, left: box.left };
    },

    _findPositionOld: function (obj) {
        var top = 0, left = 0;
        if (obj.offsetParent) {
            do {
                left += obj.offsetLeft;
                top += obj.offsetTop;
                obj = obj.offsetParent;
            } while (obj);
        }
        return { top: top, left: left };
    },

    _safeParseInt: function (raw) {
        var result = parseInt(raw);
        if (isNaN(result)) {
            result = 0;
        }
        return result;
    },

    _getStyle: function (el, styleProp) {
        var y = '';
        if (el.currentStyle) {
            var fixedProp = '';
            for (var i = 0; i < styleProp.length; i++) {
                if (styleProp.charAt(i) == '-') {
                    i++;
                    fixedProp += styleProp.charAt(i).toUpperCase();
                }
                else {
                    fixedProp += styleProp.charAt(i);
                }
            }

            y = el.currentStyle[fixedProp];
        }
        else if (document.defaultView.getComputedStyle) {
            y = document.defaultView.getComputedStyle(el, null).getPropertyValue(styleProp);
        }
        return y;
    },

    _autoScale: function (thumbImg) {
        var container = this.get_element();
        var thumb = thumbImg.parentNode;
        var pageSize = this._targetControl.getPageSize(thumbImg.pageIndex);
        thumbImg.width = Math.floor(pageSize.width * this._scale);
        thumbImg.height = Math.floor(pageSize.height * this._scale);

        var scale = 1;
        if (this._autoScaleMode == 'vertical') {
            scale = (this._innerSize(container).width - this._chromeWidth(thumbImg) - this._chromeWidth(thumb)) / thumbImg.offsetWidth;
        }
        else if (this._autoScaleMode == 'horizontal') {
            scale = (this._innerSize(container).height - this._chromeHeight(thumbImg) - this._chromeHeight(thumb)) / thumbImg.offsetHeight;
        }

        thumbImg.width = Math.floor(parseInt(thumbImg.offsetWidth) * scale);
        thumbImg.height = Math.floor(parseInt(thumbImg.offsetHeight) * scale);
        //thumb.style.width = thumbImg.width + "px";
        //thumb.style.height = thumbImg.height + "px";
    },

    _loadHandler: function (e) {
        if (this._loading) {
            var loaded = this._loading;
            this._loading = null;
            $clearHandlers(loaded);
            this._autoScale(loaded);
        }
        this._loadNext();
    },

    _loadNext: function () {
        if (this._loading === null && this._loadQueue.length > 0) {
            while (this._loadQueue.length > 0) {
                var next = this._loadQueue.shift();
                if (!this._isLoaded(next)) {
                   $addHandler(next, 'load', Function.createDelegate(this, this._loadHandler));

                   var document = this._targetControl.get_document();

                   var modifiedDate = null;

                   if (document.LastModified instanceof Date)
                      modifiedDate = document.LastModified.getTime();
                   else {
                      //in some cases the LastModified is not Date type but string
                      //which has a format '/Date(1245398693390)/'
                      //therefore we convert it to Date type here
                      var date = new Date(parseInt(document.LastModified.substr(6)));
                      modifiedDate = date.getTime();
                   }

                   next.src = String.format(this._baseUrl, this._targetControl.get_document().StorageKey, this._scale, next.rotation, next.pageIndex, modifiedDate);
                    this._loading = next;
                    break;
                }
            }
        }
    },

        _getThumbUrl: function (thumb, storageKey) {
           var document = this._targetControl.get_document();

           var modifiedDate = null;

           if (document.LastModified instanceof Date)
              modifiedDate = document.LastModified.getTime();
           else {
              //in some cases the LastModified is not Date type but string
              //which has a format '/Date(1245398693390)/'
              //therefore we convert it to Date type here
              var date = new Date(parseInt(document.LastModified.substr(6)));
              modifiedDate = date.getTime();
           }

           return String.format(this._baseUrl, storageKey, this._scale, thumb.rotation, thumb.pageIndex, modifiedDate);
    },

    _isLoaded: function (thumb) {
        return (thumb && thumb.src.indexOf(this._getThumbUrl(thumb, this._targetControl.get_document().StorageKey)) > 0);
    },

    get_targetControlId: function () {
        ///<summary>Gets or sets the control id of the associated PdfViewer control.</summary>
        ///<remarks>Setting this value after the control is initialized has no effect.</remarks>
        ///<value type="String" mayBeNull="false">The control id of the associated PdfViewer control.</value>
        return this._targetControlId;
    },

    set_targetControlId: function (value) {
        if (this._targetControlId != value) {
            this._targetControlId = value;
            this.raisePropertyChanged('targetControlId');
        }
    },

    get_baseUrl: function () {
        ///<summary>Gets or sets the base URL of the service that renders the thumbnails.</summary>
        ///<remarks>The URL should be formatted so that the page number may be appended to fetch a thumbnail for a specific page.</remarks>
        ///<value type="String" mayBeNull="false">The base URL of the service that renders the thumbnails.</value>
        return this._baseUrl;
    },

    set_baseUrl: function (value) {
        if (this._baseUrl != value) {
            this._baseUrl = value;
            this.raisePropertyChanged('baseUrl');
        }
    },

    get_activeCssClass: function () {
        /// <summary>
        /// Gets or sets the CSS class to be applied to the thumbnail that corresponds to the page currently showing in the the associated PdfViewer.
        /// </summary>
        /// <value type="String">The CSS class for the active thumbnail.</value>
        return this._activeCssClass;
    },

    set_activeCssClass: function (value) {
        if (this._activeCssClass != value) {
            this._activeCssClass = value;
            this.raisePropertyChanged('activeCssClass');
        }
    },


    get_autoScaleMode: function () {
        /// <summary>
        /// Gets or sets a value indicating whether to apply autoscaling to thumbnails (true) or not (false).
        /// </summary>
        /// <value type="Boolean">A value indicating whether to apply autoscaling to thumbnails</value>
        return this._autoScaleMode;
    },

    set_autoScaleMode: function (value) {
        if (this._autoScaleMode != value) {
            this._autoScaleMode = value;
            this.raisePropertyChanged('autoScaleMode');
        }
    },

    get_scale: function () {
        /// <summary>
        /// Gets or sets the default scale to apply to thumbnails.
        /// </summary>
        /// <value type="Number">The default scale for the thumbnails.</value>
        return this._scale;
    },

    set_scale: function (value) {
        if (value > 0 && value <= 0.25) {
            this._scale = value;
            this.raisePropertyChanged('scale');
        }
    }
};

// Optional descriptor for JSON serialization.
TallComponents.ThumbnailsList.descriptor = {
    properties: [{ name: "baseUrl", type: String },
                 { name: "targetControlId", type: String },
                 { name: "activeCssClass", type: String },
                 { name: "autoScaleMode", type: String}]
};
// Register the class as a type that inherits from Sys.UI.Control.
TallComponents.ThumbnailsList.registerClass('TallComponents.ThumbnailsList', Sys.UI.Control);

if (typeof (Sys) !== 'undefined') { Sys.Application.notifyScriptLoaded(); }